﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Data;
using System.Text.RegularExpressions;
using adc;

namespace tabtool {
    //校验类型
    enum e_cond {
        cond_enum_self,   //枚举自检OK
        cond_mask_self,   //掩码自检OK
        cond_same_table,  //聚合关联校验OK
        cond_map_key,     //key检查ok
        cond_field_type,  //字段类型检查ok
        cond_enum_exist,  //填写枚举是否存在
        cond_mask_exist,  //掩码是否存在 超出范围
        cond_field_exist, //填写值是否存在在某表 eg: exist<table_name.field_name>
        cond_length,      //长度校验 eg: length<5>
        cond_value,       //值范围
    }


    class Verifier {
        public static void CheckTable(List<TableMeta> metalist, string excelDir) {
            ExcelHelper helper = new ExcelHelper();
            Dictionary<string, int> check_dic = new Dictionary<string, int>();
            //所有的校验都在这里
            //枚举表自检
            string[] enums = Directory.GetFiles(excelDir + "/Enum/", "*.xlsm", SearchOption.TopDirectoryOnly);
            foreach (var filepath in enums) {
                DataTable dt = helper.ImportExcelFile(filepath);
                for (int i = 0; i < dt.Columns.Count; i++) {
                    for (int j = 0; j < dt.Rows.Count; j++) {
                        if (i == 0) {
                            int enum_value = 0;
                            bool success = int.TryParse(dt.Rows[j].ItemArray[i].ToString(), out enum_value);
                            if (!success)
                                throw new Exception("[ENUM SELF CHECK ERROR] Table:" + Path.GetFileNameWithoutExtension(filepath) + " Value Not Int " + dt.Rows[j].ItemArray[i].ToString() + " Row:" + (j + 1).ToString() + " Col:" + (i + 1).ToString());
                        }
                        if (check_dic.ContainsKey(dt.Rows[j].ItemArray[i].ToString())) {
                            throw new Exception("[ENUM SELF CHECK ERROR] Table:" + Path.GetFileNameWithoutExtension(filepath) + " Value Repeat " + dt.Rows[j].ItemArray[i].ToString() + " Row:" + (j + 1).ToString() + " Col:" + (i + 1).ToString());
                        }
                        check_dic.Add(dt.Rows[j].ItemArray[i].ToString(), 0);
                    }
                    check_dic.Clear();
                }
            }
            //掩码表自检
            string[] masks = Directory.GetFiles(excelDir + "/Mask/", "*.xlsm", SearchOption.TopDirectoryOnly);
            foreach (var filepath in masks) {
                DataTable dt = helper.ImportExcelFile(filepath);
                for (int i = 0; i < dt.Columns.Count; i++) {
                    int last_mask = 1, now_mask = 0;
                    for (int j = 0; j < dt.Rows.Count; j++) {
                        if (i == 0) {
                            bool success = int.TryParse(dt.Rows[j].ItemArray[i].ToString(), out now_mask);
                            if (success) {
                                if (j != 0) {
                                    if (now_mask / last_mask != 2) {
                                        throw new Exception("[MASK SELF CHECK ERROR] Table:" + Path.GetFileNameWithoutExtension(filepath) + " Value Not " + (last_mask *= 2).ToString() + " Row:" + (j + 1).ToString() + " Col:1");
                                    }
                                } else {
                                    if (now_mask != 1) {
                                        throw new Exception("[MASK SELF CHECK ERROR] Table:" + Path.GetFileNameWithoutExtension(filepath) + " Value Not 1 Row:" + (j + 1).ToString() + " Col:1");
                                    }
                                }
                                last_mask = now_mask;
                            } else {
                                throw new Exception("[MASK SELF CHECK ERROR] Table:" + Path.GetFileNameWithoutExtension(filepath) + " Value Not Int " + dt.Rows[j].ItemArray[i].ToString() + " Row:" + (j + 1).ToString() + " Col:" + (i + 1).ToString());
                            }
                        }
                        if (check_dic.ContainsKey(dt.Rows[j].ItemArray[i].ToString())) {
                            throw new Exception("[Mask SELF CHECK ERROR] Table:" + Path.GetFileNameWithoutExtension(filepath) + " Value Repeat " + dt.Rows[j].ItemArray[i].ToString() + " Row:" + (j + 1).ToString() + " Col:" + (i + 1).ToString());
                        }
                        check_dic.Add(dt.Rows[j].ItemArray[i].ToString(), 0);
                    }
                    check_dic.Clear();
                }
            }
            //自定义类型 是否与已有表名冲突
            foreach (var tbs in TableStruct.GetMetaList()) {
                foreach (var meta in metalist) {
                    if (tbs.TableName == meta.TableName) {
                        throw new Exception("[SAME NAME ERROR] Table:" + meta.TableName + "& Tbs:" + meta.TableName);
                    }
                }
            }
            //聚合关联校验
            foreach (var asc in CodeGen.associate_info) {
                bool find_table = false, find_field = false;
                //聚合
                if (asc[4] == ">") {                 
                    foreach (var meta in metalist) {
                        if(asc[2] == meta.TableName) {
                            find_table = true;
                            foreach(var f in meta.Fields) {
                                if (asc[3] == f.fieldName)
                                    find_field = true;
                            }          
                            if (!find_field) {
                                throw new Exception("[ASC CHECK CAN`T FIND FIELD] Table:" + asc[2] + " Field:" + asc[3]);
                            }
                        }
                        if(asc[0] == meta.TableName) {
                            throw new Exception("[ASC CHECK SAME NAME ERROR] Table:" + meta.TableName + "& Asc:" + meta.TableName);
                        }
                    }
                    if (!find_table) {
                        throw new Exception("[ASC CHECK CAN`T FIND TABLE] Table:" + asc[2]);
                    }
                    foreach (var tbs in TableStruct.GetMetaList()) {
                        if (asc[0] == tbs.TableName) {
                            throw new Exception("[ASC CHECK SAME NAME ERROR] Tbs:" + asc[0] + "& Asc:" + asc[0]);
                        }
                    }
                } else {
                    bool find_table2 = false, find_field2 = false;
                    foreach (var meta in metalist) {                  
                        if (asc[2] == meta.TableName) {
                            find_table = true;
                            foreach (var f in meta.Fields) {
                                if (asc[3] == f.fieldName)
                                    find_field = true;
                            }
                            if (!find_field) {
                                throw new Exception("[ASC CHECK CAN`T FIND FIELD] Table:" + asc[2] + " Field:" + asc[3]);
                            }
                        }
                        if (asc[0] == meta.TableName) {
                            find_table2 = true;
                            foreach (var f in meta.Fields) {
                                if (asc[1] == f.fieldName)
                                    find_field2 = true;
                            }
                            if (!find_field2) {
                                throw new Exception("[ASC CHECK CAN`T FIND FIELD] Table:" + asc[0] + " Field:" + asc[1]);
                            }
                        }
                    }
                    foreach (var _asc in CodeGen.associate_info) {
                        if(_asc[4] == ">" && _asc[0] == asc[0]) {
                            find_table2 = true;
                            if(asc[1] == _asc[1]) find_field2 = true;
                            if (!find_field2) {
                                throw new Exception("[ASC CHECK CAN`T FIND FIELD] Table:" + asc[0] + " Field:" + asc[1]);
                            }
                        }
                    }
                    if (!find_table2) {
                        throw new Exception("[ASC CHECK CAN`T FIND TABLE] Table:" + asc[0]);
                    }
                    if (!find_table) {
                        throw new Exception("[ASC CHECK CAN`T FIND TABLE] Table:" + asc[2]);
                    }
                }
            }
            //条件检验
            foreach (var meta in metalist) {
                //容器类型为type 检测主键是否重复
                if (meta.container_type == TableFieldType.map_type) {
                    check_dic.Clear();
                    for (int k = 3; k < meta.dt.Rows.Count; ++k) {
                        var content = meta.dt.Rows[k].ItemArray[0].ToString();
                        if (check_dic.ContainsKey(content)) {
                            throw new Exception("[SAME KEY CHECK ERROR] Table:" + meta.TableName + " Value Repeat " + content + " Row:" + (k + 1).ToString() + " Col:0");
                        }
                        check_dic.Add(content, 0);
                    }
                }
                for (int i = 0; i < meta.Fields.Count(); ++i) {
                    var field = meta.Fields[i];
                    string check_info = null;
                    //检查自定义类型是否存在
                    if (field.type.type == adc.e_type.struct_type) {
                        var tbs = TableStruct.GetTableMetaByName(((struct_type)field.type).struct_name);
                        if (tbs == null)
                            throw new Exception("[FIELD CHECK ERROR] Table:" + meta.TableName + " Field:" + field.fieldName + " Undefined Type " + meta.dt.Rows[1].ItemArray[i] + " Row:1" + " Col:" + (i + 1).ToString());
                    }
                    //检查字段所填枚举是否存在
                    if (field.type.type == adc.e_type.enum_type) {
                        string table_name = ((enum_type)field.type).table_name;
                        string filepath = excelDir + "/Enum/" + table_name + ".xlsm";
                         DataTable check_dt = helper.ImportExcelFile(filepath);
                        for (int k = 3; k < meta.dt.Rows.Count; ++k) {
                            bool find_value = false;
                            for (int m = 0; m < check_dt.Rows.Count; ++m) {
                                if (meta.dt.Rows[k].ItemArray[i].ToString() == check_dt.Rows[m].ItemArray[2].ToString()) {
                                    meta.dt.Rows[k][i] = check_dt.Rows[m].ItemArray[0].ToString();
                                    find_value = true;
                                    break;
                                }
                            }
                            if (!find_value) {
                                throw new Exception("[ENUM CHECK ERROR] Table:" + table_name + " Field:" + field.fieldName + " Not Find Value " + meta.dt.Rows[k].ItemArray[i] + " Row:" + (k + 1).ToString() + " Col:" + (i + 1).ToString());
                            }
                        }
                        continue;
                    }
                    //检查字段所填掩码是否存在
                    if (field.type.type == adc.e_type.mask_type) {
                        string table_name = ((mask_type)field.type).table_name;
                        string filepath = excelDir + "/Mask/" + table_name + ".xlsm";
                        DataTable check_dt = helper.ImportExcelFile(filepath);
                        int check_int = 0, mask = 0;
                        for (int m = 0; m < check_dt.Rows.Count; ++m) {
                            if (int.TryParse(check_dt.Rows[m].ItemArray[0].ToString(), out mask)) {
                                check_int += mask;
                            } else {
                                throw new Exception("[MASK CHECK ERROR] Table:" + table_name + " Value Not Int " + meta.dt.Rows[m].ItemArray[0] + " Row:" + (m + 1).ToString() + " Col:1");
                            }
                        }
                        for (int k = 3; k < meta.dt.Rows.Count; ++k) {
                            if (int.TryParse(meta.dt.Rows[k].ItemArray[i].ToString(), out mask)) {
                                if (mask > check_int || mask < 0)
                                    throw new Exception("[MASK CHECK ERROR] Table:" + meta.TableName + " Field:" + field.fieldName + " Value Out Of Mask Range ! " + meta.dt.Rows[k].ItemArray[i] + " Row:" + (k + 1).ToString() + " Col:" + (i + 1).ToString());
                            } else {
                                throw new Exception("[MASK CHECK ERROR] Table:" + meta.TableName + " Field:" + field.fieldName + " Value Not Int " + meta.dt.Rows[k].ItemArray[i] + " Row:" + (k + 1).ToString() + " Col:" + (i + 1).ToString());
                            }
                        }
                        continue;
                    }
                    //检查该字段所填数据是否在XXX表中
                    //eg: exist head.id
                    if ((check_info = field.Get("exist")) != null) {
                        string[] c = check_info.Split('.');
                        if (c.Count() != 2) {
                            throw new Exception("[EXIST CHECK ERROR] Table:" + meta.TableName + " Field:" + field.fieldName + " ParamNum Not Two!!!");
                        }
                        string[] igs = {""};
                        var ignore = field.Get("ignore");
                        if(ignore != null) {
                            igs = ignore.Split('|');
                        }

                        //找到校验dt
                        DataTable check_dt = null;
                        int check_col = -1;
                        foreach (var me in metalist) {
                            if (me.TableName.ToLower() == c[0].ToLower()) {
                                check_dt = me.dt;
                                for (int j = 0; j < me.dt.Columns.Count; ++j) {
                                    if (me.dt.Rows[0].ItemArray[j].ToString() == c[1].ToLower()) {
                                        check_col = j;
                                        break;
                                    }
                                }
                                break;
                            }
                        }
                        if (check_dt == null) {
                            throw new Exception("[EXIST CHECK ERROR] Table:" + meta.TableName + " Field:" + field.fieldName + " Not Find Table " + c[0]);
                        }
                        if (check_col == -1) {
                            throw new Exception("[EXIST CHECK ERROR] Table:" + meta.TableName + " Field:" + field.fieldName + " Not Find Field " + c[1]);
                        }
                        //遍历dt这一列
                        bool find_value = false;
                        for (int k = 3; k < meta.dt.Rows.Count; ++k) {
                            if (field.type.type == e_type.list_type) {
                                string[] s = meta.dt.Rows[k].ItemArray[i].ToString().Split('|');
                                foreach (var v in s) {
                                    for (int m = 3; m < check_dt.Rows.Count; ++m) {
                                        foreach (var ig in igs) {
                                            if (ig == v) {
                                                find_value = true;
                                                break;
                                            }
                                        }
                                        if (find_value) continue;
                                        if (v == check_dt.Rows[m].ItemArray[check_col].ToString()) {
                                            find_value = true;
                                            break;
                                        }
                                    }
                                    if (!find_value) {
                                        break;
                                    }
                                }
                            } else {
                                for (int m = 3; m < check_dt.Rows.Count; ++m) {
                                    foreach (var ig in igs) {
                                        if (ig == meta.dt.Rows[k].ItemArray[i].ToString()) {
                                            find_value = true;
                                            break;
                                        }
                                    }
                                    if (find_value) break;
                                    if (meta.dt.Rows[k].ItemArray[i].ToString() == check_dt.Rows[m].ItemArray[check_col].ToString()) {
                                        find_value = true;
                                        break;
                                    }
                                }
                            }
                            if (!find_value) {
                                throw new Exception("[EXIST CHECK ERROR] Table:" + meta.TableName + " Field:" + field.fieldName + " Not Find Value " + meta.dt.Rows[k].ItemArray[i] + " Row:" + (k + 1).ToString() + " Col:" + (i + 1).ToString());
                            }
                        }
                    }
                    //检查该字段所填数据长度是否正确
                    //eg: length 5
                    if ((check_info = field.Get("length")) != null) {
                        for (int k = 3; k < meta.dt.Rows.Count; ++k) {
                            int check_num = 0;
                            bool ret = int.TryParse(check_info, out check_num);
                            if (!ret) {
                                throw new Exception("[LENGTH CHECK ERROR] Table:" + meta.TableName + " Field:" + field.fieldName + " Unrecognized " + check_info + " Row:" + (k + 1).ToString() + " Col:" + (i + 1).ToString());
                            }
                            if (!(meta.dt.Rows[k].ItemArray[i].ToString().Split('|').Count() == check_num) ) {
                                throw new Exception("[LENGTH CHECK ERROR] Table:" + meta.TableName + " Field:" + field.fieldName + " Count Not Equal " + check_info + " Row:" + (k + 1).ToString() + " Col:" + (i + 1).ToString());
                            }
                        }
                    }
                    //检查该字段所填值是否正确 值校验 大小 是否越界
                    //TODO:
                    //暂时还没想好 想好再说
                    if ((check_info = field.Get("value")) == null) {



                    }                    
                }
            }
        }
    }
}